package cn.hy.shortUrl.service;

import cn.hy.shortUrl.entity.UrlMap;
import cn.hy.shortUrl.manager.UrlMapManager;
import cn.hy.shortUrl.mapper.UrlMapMapper;
import cn.hy.shortUrl.util.KeyUtils;
import com.github.benmanes.caffeine.cache.*;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

// @Service
public class ShortLinkV2ServiceImpl implements ShortLinkService {

    // 单机用模拟， 多实例可以redis 做，不影响id 会被随机掉
    private AtomicLong longAdder = new AtomicLong(1000000);
    @Resource
    private UrlMapMapper urlMapMapper;
    @Resource
    private UrlMapManager urlMapManager;

    @Value("${shortLink.write.cache.enable:false}")
    private boolean enableWriteCache;

    private Cache<String, String> writeCache = Caffeine.newBuilder()
            .initialCapacity(100)
            .maximumSize(10000)
            .expireAfterAccess(1, TimeUnit.HOURS)
            .removalListener(new RemovalListener<String, String>() {
                @Override
                public void onRemoval(@Nullable String key, @Nullable String value, @NonNull RemovalCause cause) {
                    System.out.println("...");
                }
            })
            .build();


    private static final Cache<String, String> readCache = Caffeine.newBuilder()
            .initialCapacity(10000)
            .maximumSize(10000000)
            .expireAfterAccess(1, TimeUnit.DAYS)
            .build();

    /**
     * <br> 这里我指的是在分库分表的场景中，我们只能使用 key 来查找，已经不支持使用 original_url 进行数据库查找了。
     *  至于写请求，接口来了一个 originalUrl，我们不能去数据库中查询是否已经有这条记录，
     *  所以两条一模一样的链接我们会生成两个不一样的短链接，当然，通常我们也是允许这种情况的。
     *
     * 由于生成短链的接口一般是提供给其他各个业务系统使用的，所以其实可以由调用方来决定是否要使用写缓存，
     * 这样能得到最好的效果。如果调用方知道自己接下来需要批量转换的长链是不会重复的，那么调用方可以设置不使用缓存，而对于一般性的场景，默认开启写缓存。
     * @param url 长链接
     * @return 短key
     */
    @Override
    public String save(String url) {
        long id = longAdder.incrementAndGet();
        String key = KeyUtils.toBase62(id);
        if (enableWriteCache) {
            writeCache.put(url,key);
             //todo 优化
            new Thread(()->{
                saveInDB(url, key);
            }).start();
            return key;
        }
        saveInDB(url, key);
        return key;
    }

    private void saveInDB(String url, String key) {
        String lookup = lookup(key);
        if (StringUtils.isNotEmpty(lookup)) {
            throw new RuntimeException("短链接已经存在");
        }
        UrlMap record = UrlMap.builder().sortUrl(key).longUrl(url).views(0).build();
        urlMapMapper.insert(record);
    }

    @Override
    public String lookup(String key) {
        return readCache.get(key, cacheKey -> urlMapManager.getLongUrlByKey(cacheKey));
    }

}
